package com.example.util;

import ch.qos.logback.classic.Logger;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class XmlFill {

	private static XmlFill tplm = null;
	private Configuration cfg = null;
	private final static Logger logger = (Logger) LoggerFactory.getLogger(XmlFill.class);

	private XmlFill(Environment environment) {
		cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
		try {
			environment.getProperty("TEMPLATES",String.class);
			// 注册tmlplate的load路径
			cfg.setDirectoryForTemplateLoading(new File(
					environment.getProperty("TEMPLATES",String.class)
			));
			cfg.setDefaultEncoding("UTF-8");
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
	}

	/**
	 * 另一种获取freemaker模板的方式
	 */
	private XmlFill(){
		cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
		try{
			TemplateLoader templateLoader = new FileTemplateLoader(
					new File(XmlFill.class.getClassLoader().getResource("./templates").getPath()));
			cfg.setTemplateLoader(templateLoader);
		}catch (Exception e){
			logger.error(e.getMessage());
		}
	}

	private static Template getTemplate(String name,Environment environment) throws IOException {
		if (tplm == null) {
			tplm = new XmlFill(environment);
		}
		return tplm.cfg.getTemplate(name,"UTF-8");
	}

	/**
	 * 
	 * @param templatefile
	 *            模板文件
	 * @param param
	 *            需要填充的内容
	 * @param out
	 *            填充完成输出的文件
	 * @throws IOException
	 * @throws TemplateException
	 */
	public static void process(String templatefile, Map<String, String> param, Writer out,Environment environment)
			throws IOException, TemplateException {
		// 获取模板
		Template template = XmlFill.getTemplate(templatefile,environment);
		template.setOutputEncoding("UTF-8");
		// 合并数据
		template.process(param, out);
		if (out != null) {
			out.close();
		}
	}

	public static Template getTemplate(String name) throws Exception{
		if(tplm == null){
			tplm = new XmlFill();
		}
		return tplm.cfg.getTemplate(name,"UTF-8");
	}

    /**
     * poi 根据模板获取word文件
     * @param file
     * @param textMap
     */
    public static void poiExportNewWord(File file,Map<String,String> textMap,List<Map> checkResult,Environment environment,String templateName) throws Exception{
        String inputUrl = environment.getProperty("TEMPLATES",String.class) + templateName;
        try{
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));

            //解析替换文本段落对象
            changeText(document, textMap,checkResult);

            //解析替换表格对象
            changeTable(document,textMap);

            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();
        }catch (Exception e){
            logger.error("poi根据模板获取文件出现异常。",e);
            throw e;
        }

    }

    /**
     * poi 根据模板获取word文件
     * @param outputUrl
     * @param textMap
     */
	public static void poiExportNewWord(String outputUrl,Map<String,String> textMap,List<Map> checkResult,Environment environment,String templateName) throws Exception{
		String inputUrl = environment.getProperty("TEMPLATES",String.class) + templateName;
	    try{
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));

            //解析替换文本段落对象
            changeText(document, textMap,checkResult);

            //解析替换表格对象
            changeTable(document,textMap);

            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();
        }catch (Exception e){
            logger.error("poi根据模板获取文件出现异常。",e);
            throw e;
        }

    }


    /**
     * 按需维保表格参数，新增动态表格（专用于按需维保）
     * @param outputUrl
     * @param textMap
     * @param checkResult
     * @param environment
     * @param templateName
     * @param otherParams 动态新增表格参数
     * @throws Exception
     */
    public static void poiExportNewWord(String outputUrl,Map<String,String> textMap,List<Map> checkResult,Environment environment,String templateName,Map<String,Object> otherParams) throws Exception{
        String inputUrl = environment.getProperty("TEMPLATES",String.class) + templateName;
        try{
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));

            //解析替换文本段落对象
            changeText(document, textMap,checkResult);

            //解析替换表格对象
            changeTable(document,textMap,otherParams);

            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();
        }catch (Exception e){
            logger.error("poi根据模板获取文件出现异常。",e);
            throw e;
        }

    }

    /**
     * poi 导出word 替换段落文本
     * @param document docx解析对象
     * @param textMap 需要替换的信息集合
     * @param checkResult 特殊处理时传入的值，其他可不传
     */
    private static void changeText(XWPFDocument document, Map<String, String> textMap,List<Map> checkResult) throws Exception{

        //获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (XWPFParagraph paragraph : paragraphs) {
            //判断此段落时候需要进行替换
            String text = paragraph.getText();
            //处理法律条文
			if(text.indexOf("上述问题违反了") != -1){  //指令书的法律条文的特殊处理
				wordAddUnderLine(paragraph,checkResult);
				continue;
			}
            if(checkText(text)){
                List<XWPFRun> runs = paragraph.getRuns();
//                StringBuilder str = new StringBuilder();
                for (XWPFRun run : runs) {
                    //替换模板原来位置
                    if(run == null || "".equals(run)){
                        continue;
                    }
                    run.setText(changeValue(run.toString(), textMap),0);

                }
            }
        }
    }


    /**
     * 按需维保使用
     * @param document
     * @param textMap
     */
    private static void changeTable(XWPFDocument document, Map<String, String> textMap,Map<String,Object> otherParams) throws Exception{
        List<XWPFTable> tables = document.getTables();

        for(int i = 0, len = tables.size(); i < len ; i ++){
            XWPFTable table = tables.get(i);
            if(checkText(table.getText())) {
                List<XWPFTableRow> rows = table.getRows();
                //遍历表格,并替换模板
                eachTable(document, rows, textMap);
            }
        }
        XWPFTable table = tables.get(0);
        List<String[]> elevatorList = (List<String[]>) otherParams.get("elevatorList");
        List<String[]> insuranceList = (List<String[]>) otherParams.get("insuranceList");

        insertTable(table, elevatorList,insuranceList);

    }


    /**
     *  为表格插入数据，行数不够添加新行
     * @param table
     * @param elevatorList
     * @param insuranceList
     */
    public static void insertTable(XWPFTable table, List<String []> elevatorList,List<String[]> insuranceList){
        int height = table.getRow(0).getHeight();
        boolean isHandle = true;
        for(int i = 0; i < table.getNumberOfRows() ; i++){
            XWPFTableRow row = table.getRow(i);
            XWPFTableCell cell00 = row.getCell(0);

            //处理多个保险单位的情况
            if(isHandle && StringUtils.isNotEmpty(cell00.getText()) && cell00.getText().contains("电梯安全综合保险有效期") && insuranceList.size() > 2){
                XWPFTableCell cell01 = row.getCell(1);
                for(int j = 2,len1 = insuranceList.size(); j < len1 ; j++){
                    XWPFTableRow row1 = table.insertNewTableRow(i+j-1);
                    XWPFTableCell cell1 =  row1.createCell();
                    XWPFTableCell cell2 =  row1.createCell();
                    cell1.getCTTc().setTcPr(cell00.getCTTc().getTcPr());
                    cell2.getCTTc().setTcPr(cell01.getCTTc().getTcPr());
                    row1.setHeight(height);
                }

                for(int j = 2,len1 = insuranceList.size(); j < len1 ; j++){
                    List<XWPFTableCell> cells = table.getRow(i+j-1).getTableCells();
                    for(int k = 0; k < cells.size(); k++){
                        XWPFTableCell cell1 = cells.get(k);
                        XWPFParagraph cellParagraphC = cell1.getParagraphs().get(0);
                        cellParagraphC.setAlignment(ParagraphAlignment.CENTER); //设置表格内容居中
                        XWPFRun cellParagraphRunC  = cellParagraphC.createRun();
                        cellParagraphRunC.setText(insuranceList.get(j)[k]); //单元格段落加载内容

                        if(k == 0){
                            //设置字体
                            cellParagraphRunC.setFontSize(14); //设置表格内容字号
                        }
                    }
                }
                isHandle = false;
            }

            //处理多条电梯数据的情况
            if(StringUtils.isNotEmpty(cell00.getText()) && cell00.getText().contains("序号") && elevatorList.size() > 1){
                XWPFTableCell cell01 = row.getCell(1);
                XWPFTableCell cell02 = row.getCell(2);
                XWPFTableCell cell03 = row.getCell(3);
                for(int j = 1,len2 = elevatorList.size(); j < len2 ; j++){
                    XWPFTableRow row1 = table.insertNewTableRow(i+j+1);
                    XWPFTableCell cell1 =  row1.createCell();
                    XWPFTableCell cell2 =  row1.createCell();
                    XWPFTableCell cell3 =  row1.createCell();
                    XWPFTableCell cell4 =  row1.createCell();
                    cell1.getCTTc().setTcPr(cell00.getCTTc().getTcPr());
                    cell2.getCTTc().setTcPr(cell01.getCTTc().getTcPr());
                    cell3.getCTTc().setTcPr(cell02.getCTTc().getTcPr());
                    cell4.getCTTc().setTcPr(cell03.getCTTc().getTcPr());
                    row1.setHeight(height);
                }
                for(int j = 1,len2 = elevatorList.size(); j < len2 ; j++){
                    List<XWPFTableCell> cells = table.getRow(i+j+1).getTableCells();
                    for(int k = 0; k < cells.size(); k++){
                        XWPFTableCell cell2 = cells.get(k);
                        cell2.setText(elevatorList.get(j)[k]);
                        //设置居中
                        CTTc cttc = cell2.getCTTc();
                        CTTcPr ctPr = cttc.addNewTcPr();
                        ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                        cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
                    }
                }
            }

        }
    }

    /**
     * 替换表格对象方法
     * @param document
     * @param textMap
     */
    private static void changeTable(XWPFDocument document, Map<String, String> textMap) throws Exception{
        List<XWPFTable> tables = document.getTables();
        for(int i = 0, len = tables.size(); i < len ; i ++){
            XWPFTable table = tables.get(i);
            if(checkText(table.getText())) {
                List<XWPFTableRow> rows = table.getRows();
                //遍历表格,并替换模板
                eachTable(document, rows, textMap);
            }
        }
    }

    /**
     * 遍历表格
     * @param rows 表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(XWPFDocument document,List<XWPFTableRow> rows ,Map<String, String> textMap) throws Exception{
        for(XWPFTableRow row : rows){
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                //判断单元格是否需要替换
                if(checkText(cell.getText())){
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        StringBuilder builder = new StringBuilder();
                        boolean isReplace = false;
                        for (XWPFRun run : runs) {
                            if(run == null || "".equals(run)){
                                continue;
                            }

                            Object ob = null;
                            builder.append(run.toString());

                            if(builder.indexOf("${") == -1 && builder.indexOf("$}") == -1){
                                builder = new StringBuilder();
                                continue;
                            }else{
                                if(builder.indexOf("${") != -1){

                                    String tempValue = run.toString();
                                    if(builder.indexOf("}") == -1){
                                        if(isReplace){
                                            run.setText("",0);
                                        }else{
                                            String str = tempValue.replace(tempValue.substring(tempValue.indexOf("${")),"");
                                            run.setText(str,0);
                                            isReplace = true;
                                        }
                                    }else{
                                        String str = tempValue.replace(tempValue.substring(0,tempValue.indexOf("}") + 1),"");
                                        run.setText(str,0);
                                        ob = changeValue(builder.toString(), textMap);
                                        builder = new StringBuilder();
                                    }

                                } 
                            }
                            if (ob != null){
                                run.setText((String)ob,0);
                            }
                        }
                    }
                }
            }
        }
    }


	/**
	 * 指令书专用方法，法律条文动态新增添加下划线
	 */
	private static void wordAddUnderLine(XWPFParagraph paragraph,List<Map> checkResult) throws Exception{
        int lawCount = 1;
        List<String> alreadyExist = new ArrayList<>();

        for(int i = 0,len = checkResult.size(); i < len; i++){
            Map map = checkResult.get(i);
            //拼接指令书中违法的法律
            if(map.get("violationLaws") != null && !"".equals(map.get("violationLaws")) && !alreadyExist.contains(map.get("code") + "")){
                if(lawCount == 1){
                    XWPFRun r1 = paragraph.createRun();
                    r1.setText("《特种设备安全法》第");
                    XWPFRun r2 = paragraph.createRun();
                    r2.setText("  "+map.get("violationLaws") + "  ");
                    r2.setUnderline(UnderlinePatterns.SINGLE);
                    XWPFRun r3 = paragraph.createRun();
                    r3.setText("条");
                    XWPFRun r4 = paragraph.createRun();
                    r4.setText("  " + map.get("violationLawsContent") + "  ");
                    r4.setUnderline(UnderlinePatterns.SINGLE);
                }else{
                    XWPFRun r5 = paragraph.createRun();
                    r5.setText("；第");
                    XWPFRun r6 = paragraph.createRun();
                    r6.setText("  "+map.get("violationLaws") + "  ");
                    r6.setUnderline(UnderlinePatterns.SINGLE);
                    XWPFRun r7 = paragraph.createRun();
                    r7.setText("条");
                    XWPFRun r8 = paragraph.createRun();
                    r8.setText("  " + map.get("violationLawsContent") + "  ");
                    r8.setUnderline(UnderlinePatterns.SINGLE);
                }
                lawCount++;
                alreadyExist.add(map.get("code") + "");
            }
        }
        XWPFRun r9 = paragraph.createRun();
        r9.setText("的规定。");
        alreadyExist.clear();
        lawCount = 1;
        for(int i = 0,len = checkResult.size(); i < len; i++){
            Map map = checkResult.get(i);
            //拼接指令书中违法执法依据
            if(map.get("violationLaws") != null && !"".equals(map.get("violationLaws")) && !alreadyExist.contains(map.get("code") + "")){
                if(lawCount == 1){
                    XWPFRun r1 = paragraph.createRun();
                    r1.setText("根据《特种设备安全法》第");
                    XWPFRun r2 = paragraph.createRun();
                    r2.setText("  " + map.get("lawEnforcementBasis") + "  ");
                    r2.setUnderline(UnderlinePatterns.SINGLE);
                    XWPFRun r3 = paragraph.createRun();
                    r3.setText("条");
                    XWPFRun r4 = paragraph.createRun();
                    r4.setText("  " + map.get("lawEnforcementBasisContent") + "  ");
                    r4.setUnderline(UnderlinePatterns.SINGLE);
                }else{
                    XWPFRun r5 = paragraph.createRun();
                    r5.setText("；第");
                    XWPFRun r6 = paragraph.createRun();
                    r6.setText("  " + map.get("lawEnforcementBasis") + "  ");
                    r6.setUnderline(UnderlinePatterns.SINGLE);
                    XWPFRun r7 = paragraph.createRun();
                    r7.setText("条");
                    XWPFRun r8 = paragraph.createRun();
                    r8.setText("  " + map.get("lawEnforcementBasisContent") + "  ");
                    r8.setUnderline(UnderlinePatterns.SINGLE);
                }
                lawCount++;
                alreadyExist.add(map.get("code") + "");
            }
        }
        XWPFRun r10 = paragraph.createRun();
        r10.setText("的规定，");

        List<XWPFRun> runs = paragraph.getRuns();
        for(int i = 0, len = runs.size(); i < len; i++){
            if(i == 0){
                continue;
            }
            XWPFRun run = runs.get(i);
            run.setFontSize(11);
            CTRPr rpr = run.getCTR().isSetRPr() ? run.getCTR().getRPr() : run.getCTR().addNewRPr();
            CTFonts fonts = rpr.isSetRFonts() ? rpr.getRFonts() : rpr.addNewRFonts();
            fonts.setAscii("楷体");
            fonts.setEastAsia("楷体");
            fonts.setHAnsi("楷体");
        }
	}


    /**
     * 匹配传入信息集合与模板
     * @param value 模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    private static String changeValue(String value, Map<String, String> textMap) throws Exception{
        Set<Entry<String, String>> textSets = textMap.entrySet();
        for (Entry<String, String> textSet : textSets) {
            //匹配模板与替换值 格式${key}
            String key = "${"+textSet.getKey()+"}";
            if(value.indexOf(key)!= -1){
                value = value.replace(key,textSet.getValue());
                return value;
            }
        }
        //模板未匹配到区域替换为空
        if(checkText(value)){
            value = "";
        }
        return value;
    }


    /**
     * 判断文本中时候包含$
     * @param text 文本
     * @return 包含返回true,不包含返回false
     */
    private static boolean checkText(String text){
        boolean check  =  false;
        if(text.indexOf("$")!= -1){
            check = true;
        }
        return check;

    }

}
